#include<stdio.h>
#include<sys/types.h>
#include<libgen.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<getopt.h>
#include<unistd.h>
#include<time.h>
#include<sys/select.h>
#include<stdlib.h>
#define LISTEN_PORT	9999
#define BACKLOG		13

#define ARRAYSIZE(x)	(sizeof(x)/sizeof(x[0]))

static inline void printf_usage(char* progname)
{
	printf("Print progname[%s] [-p] port | [-h] help | [-d] daemon\n",progname);
}

static inline int server_init(char *serv_ip,int serv_port)
{
	int			listen_fd;
	struct sockaddr_in	serv_addr;
	int			rv=0;
	int			on=1;


	//服务器端 首先创建socket  ipv4 tcp 0
	listen_fd=socket(AF_INET,SOCK_STREAM,0);
	if(listen_fd<0)
	{
		printf("Create a socket failure: %s!\n",strerror(errno));
		return -1;
	}
	printf("Create a socket[%d] successfully!\n",listen_fd);

	setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	//第二步，重置服务器端的数据 为什么这里求大小用的是指针呢？
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(serv_port);
	if(!serv_ip)
	{
		serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	}
	else
	{
		if(inet_pton(AF_INET,serv_ip,&serv_addr.sin_addr)<=0)
		{
			printf("inet_pton() set listen IP address failure.\n");
			rv=-2;
			goto CleanUp;
		}
	}


	//第三步步 bind
	if((bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0))
	{
		printf("Create a socket[%d] failure: %s!\n",listen_fd,strerror(errno));
		rv=-2;
		goto CleanUp;
	}
	printf("Socket[%d] bind on port[%d] for all IP ok!\n",listen_fd,LISTEN_PORT);

	//第四步 服务器监听
	listen(listen_fd,BACKLOG);
CleanUp:
	if(rv<0)
		close(listen_fd);	
	else
		return listen_fd;

}

static inline void msleep(unsigned long ms)
{
	struct timeval		tv;
	tv.tv_sec=ms/1000;
	tv.tv_usec=(ms%1000)*1000;
	select(0,NULL,NULL,NULL,&tv);
}
int main(int argc,char **argv)
{
	int 			rv=-1;
	int			connfd;
	int			listen_fd=-1;
	int			max_fd=-1;
	char*			progname;
	char 			buf[1024];
	fd_set			rd_set;
	int			serv_port=-1;
	int			opt=-1;
	int			daemon_run=0;
	int			fds_array[1024];
	struct option		long_options[]=	{
		{"port",required_argument,NULL,'p'},
		{"daemon",no_argument,NULL,'d'},
		{"help",no_argument,NULL,'h'},
		{NULL,0,NULL,0}
	};

	//首先，接收用户输入的指令
	progname=basename(argv[0]);
	if((opt=getopt_long(argc,argv,"bp:h",long_options,NULL))!=-1)
	{
		switch(opt)
		{
			case 'h':
				printf_usage(progname);
				break;
			case 'd':
				daemon_run=1;
				break;
			case 'p':
				serv_port=atoi(optarg);
				break;
			default:
				break;

		}

	}

	if(!serv_port)
	{
		printf_usage(progname);
		return -1;
	}
	printf("Port : %d\n",serv_port);
	//然后开始初始化服务器
	if((listen_fd=server_init(NULL,serv_port))<0)
	{
		printf("Create client_fd failure: %s\n",strerror(errno));
		return -2;
	}
	printf("%s server start to listen on port %d\n",argv[0],serv_port);

	if(daemon_run)
	{
		daemon(0,0);
	}
	printf("开始进入后台\n");
	for(int i=0;i<ARRAYSIZE(fds_array);i++)
	{
		fds_array[i]=-1;
	}
	fds_array[0]=listen_fd;
	printf("Init successfully!\n");
	while(1)
	{
		FD_ZERO(&rd_set);
		printf("ARRAYSIZE: %ld\n",ARRAYSIZE(fds_array));
		for(int i=0;i<ARRAYSIZE(fds_array);i++)
		{
			if(fds_array[i]<0)
				continue;
			max_fd=fds_array[i]>max_fd?fds_array[i]:max_fd;
			FD_SET(fds_array[i],&rd_set);
		}
		rv=select(max_fd+1,&rd_set,NULL,NULL,NULL);
		if(rv<0)
		{
			printf(" Create a select failure: %s\n",strerror(errno));
			continue;
		}
		else if(0==rv)
		{
			printf("client get disconnected!\n");
			continue;
		}

		printf("Create select successfully\n");
		//如果listen_fd在rd_set内 那么创建一个新的conn_fd
		if(FD_ISSET(listen_fd,&rd_set))
		{
			printf("Waiting for client to connect...\n");
			connfd=accept(listen_fd,(struct sockaddr*)NULL,NULL);
			if(connfd<0)
			{
				printf("Connect to client failure!\n");
				continue;
			}
			int found=0;
			//在fds_array中找到位置来放listen_fd
			for(int i=0;i<ARRAYSIZE(fds_array);i++)
			{
				if(fds_array[i]<0)
				{
					printf("Accpet new client [%d] and add it into array\n",connfd);
					found=1;
					fds_array[i]=connfd;
					break;
				}
			}	

			if(!found)
			{
				printf("Accept new client[%d],but array is full,so refuse\n",connfd);
				close(connfd);
			}

		}
		else
		{
			for(int i=0;i<ARRAYSIZE(fds_array);i++)
			{
				//此时看的是fds_array里的connfd
				//如果不在rd_set中
				if(fds_array[i]<0||!FD_ISSET(fds_array[i],&rd_set))
					continue;

				//读取文件
				memset(buf,0,sizeof(buf));
				rv=read(fds_array[i],buf,sizeof(buf));
				if(rv<0)
				{
					printf("Read data from client in socket[%d] failure!\n",fds_array[i]);
					close(fds_array[i]);
					fds_array[i]=-1;
				}
				else if(0==rv)
				{
					printf("Client get disconnected!\n");
					close(fds_array[i]);
					fds_array[i]=-1;
				}
				printf("Read data %d bytes : %s from sock[%d]!\n",rv,buf,fds_array[i]);

				if(write(fds_array[i],buf,rv)<0)
				{
					printf("Write back to client socket[%d] failure:%s!\n",fds_array[i],strerror(errno));
					close(fds_array[i]);
					fds_array[i]=-1;
				}
				printf("Write to client socket[%d] successfully!\n",fds_array[i]);
				close(fds_array[i]);
			}
		}
	}//while
CleanUp:
close(listen_fd);
return 0;	
}
